
#include "loirey_BasicStructure.h"

using namespace loirey;

const double loirey::CMyMath::MAXIMUM_LOG_RESULT_C = log(MAXIMUM_DOUBLE_NUM_C);
const double loirey::CMyMath::MINIMUM_LOG_RESULT_C = log(MINIMUM_DOUBLE_EPS_C);

bool CComparableObject::operator < (const CComparableObject& b) const
{
	return this->Compare(b)<0;
}

bool CComparableObject::operator <= (const CComparableObject& b) const
{
	return this->Compare(b)<=0;
}

bool CComparableObject::operator > (const CComparableObject& b) const
{
	return this->Compare(b)>0;
}

bool CComparableObject::operator >= (const CComparableObject& b) const
{
	return this->Compare(b)>=0;
}

bool CComparableObject::operator == (const CComparableObject& b) const
{
	return this->Compare(b)==0;
}

bool CComparableObject::operator != (const CComparableObject& b) const
{
	return this->Compare(b)!=0;
}

void CHistogramConfig::myInit(int BinAmount, bool fAreBinWrapped, double ValueBoundA, double ValueBoundB)
{
	if (BinAmount < 1) BinAmount = 1;

	this->BinAmount = BinAmount;
	this->fAreBinWrapped = fAreBinWrapped;

	ValueBase = (ValueBoundA<ValueBoundB ? ValueBoundA : ValueBoundB);
	ValueBinWidth = (ValueBoundA + ValueBoundB - ValueBase - ValueBase) / BinAmount;
	if (ValueBinWidth < EPS)
	{
		ValueBinWidth = 1.0;
		ValueBase -= 0.5 * BinAmount;
	}
}

inline int CHistogramConfig::SetBinIndexCorrect(int BinIndex) const
{
	return (
		fAreBinWrapped ? (
		(BinIndex%BinAmount+BinAmount)%BinAmount) : (
			BinIndex<0 ?
			0 : (
				BinIndex<BinAmount ?
				BinIndex : (BinAmount - 1)
		)));
}

bool CHistogramConfig::InputFromStream(istream& inStream)
{
	if (inStream>>BinAmount>>fAreBinWrapped>>ValueBase>>ValueBinWidth)
		return true;
	else
		return false;
}

void CHistogramConfig::OutputToStream(ostream& outStream) const
{
	outStream<<BinAmount<<"\t";
	outStream<<fAreBinWrapped<<"\t";
	outStream.precision(16);
	outStream<<showpos<<scientific<<ValueBase<<"\t";
	outStream<<showpos<<scientific<<ValueBinWidth<<endl;
}

double CHistogramConfig::GetBinMiddleValue(int BinIndex) const
{
	return ValueBase + ValueBinWidth * (BinIndex + 0.5);
}

double CHistogramConfig::GetBinFloorValue(int BinIndex) const
{
	return ValueBase + ValueBinWidth * (BinIndex + 0.0);
}

double CHistogramConfig::GetBinCeilingValue(int BinIndex) const
{
	return ValueBase + ValueBinWidth * (BinIndex + 1.0);
}

int CHistogramConfig::SelectBin_LowerThan(double Value) const
{
	return SetBinIndexCorrect(CMyMath::TranslateNumber_Floor((Value - ValueBase) / ValueBinWidth) - 1);
	//return SetBinIndexCorrect((int)((Value - ValueBase) / ValueBinWidth) - 1);
}

int CHistogramConfig::SelectBin_GreaterThan(double Value) const
{
	return SetBinIndexCorrect(CMyMath::TranslateNumber_Floor((Value - ValueBase) / ValueBinWidth) + 1);
	//return SetBinIndexCorrect((int)((Value - ValueBase) / ValueBinWidth) + 1);
}

int CHistogramConfig::SelectBin_Nearest(double Value) const
{
	return SetBinIndexCorrect(CMyMath::TranslateNumber_Floor((Value - ValueBase) / ValueBinWidth) + 0);
	//return SetBinIndexCorrect((int)((Value - ValueBase) / ValueBinWidth) + 0);
}

double CHistogramConfig::SelectBin_Bounding(int& BinA, int& BinB, double Value) const
{
	double ret = 0.0;

	Value = (Value - ValueBase) / ValueBinWidth - 0.5;
	
	BinB = CMyMath::TranslateNumber_Floor(Value);
	ret = Value - BinB;
	BinA = SetBinIndexCorrect(BinB);
	BinB = SetBinIndexCorrect(BinB + 1);

	//BinB = (int)Value;
	//if (Value < 0)
	//{
	//	ret = Value - (BinB - 1);
	//	BinA = SetBinIndexCorrect(BinB - 1);
	//	BinB = SetBinIndexCorrect(BinB - 0);
	//}
	//else
	//{
	//	ret = Value - (BinB + 0);
	//	BinA = SetBinIndexCorrect(BinB + 0);
	//	BinB = SetBinIndexCorrect(BinB + 1);
	//}

	if (BinA == BinB)
		ret = 0.0;
	return ret;
}

// [*] CHistogramDistribution

void CHistogramDistribution::InitHistogramConfig(int BinAmount, bool fAreBinWrapped, double ValueBoundA, double ValueBoundB)
{
	this->BinAmount = BinAmount;
	HistogramConfig.myInit(BinAmount, fAreBinWrapped, ValueBoundA, ValueBoundB);
	Clear();
}

void CHistogramDistribution::Clear()
{
	BinWeightDistribution.Resize(BinAmount);
	BinWeightDistribution.Fill(0.0);
	WeightSum = 0.0;
}

void CHistogramDistribution::NewWeightedValue(double Value, double Weight)
{
	if (Weight < MINIMUM_DOUBLE_EPS_C) return;
	WeightSum += Weight;
	BinWeightDistribution.pElement[HistogramConfig.SelectBin_Nearest(Value)] += Weight;
}

double CHistogramDistribution::GetBinMiddleValue(int BinIndex) const
{
	return HistogramConfig.GetBinMiddleValue(BinIndex);
}

void CHistogramDistribution::Analyse(
	CSimpleTypeArray<double>& BinMiddleValueList,
	CSimpleTypeArray<double>& DistributionList) const
{
	int i;
	DistributionList.Resize(BinAmount);
	BinMiddleValueList.Resize(BinAmount);
	double ss = WeightSum;
	if (ss < MINIMUM_DOUBLE_EPS_C) ss = 1.0;
	for ( i = 0; i < BinAmount; ++i )
	{
		DistributionList.pElement[i] = BinWeightDistribution.pElement[i] / ss;
		BinMiddleValueList.pElement[i] = this->GetBinMiddleValue(i);
	}
}

void CUnionSet::myRelease()
{
	_n = 0;
	CDataStructureTools::MyRelease_List(_rgParent);
}

CUnionSet::CUnionSet()
{
	_n = 0;
	_rgParent = NULL;
}

CUnionSet::~CUnionSet()
{
	myRelease();
}

void CUnionSet::myInit(int NodeAmount)
{
	_n = NodeAmount;
	_rgParent = new int[_n];
	_root_index.myInit(_n);
	Clear();
}

void CUnionSet::Clear()
{
	int i;

	_root_index.Clear();
	for ( i = 0; i < _n; ++i )
	{
		_rgParent[i] = i;
		_root_index.AddToNode(i,1);
	}
}

void CUnionSet::Merge(int NodeIndexA, int NodeIndexB)
{
	int a = FindRoot(NodeIndexA);
	int b = FindRoot(NodeIndexB);
	if (a==b) return;
	_rgParent[b] = a;
	_root_index.AddToNode(b,-1);
}

int CUnionSet::GetRootAmount()
{
	return _root_index.GetCumulativeSum(_n-1);
}

int CUnionSet::FindRoot(int NodeIndex)
{
	if (_rgParent[NodeIndex] == NodeIndex)
		return NodeIndex;
	else
		return (_rgParent[NodeIndex] = FindRoot(_rgParent[NodeIndex]));
}

int CUnionSet::GetRootIndex(int NodeIndex)
{
	int r = FindRoot(NodeIndex);
	return _root_index.GetCumulativeSum(r-1);
}

CBoundedValueHistogram::CBoundedValueHistogram()
{
	BinAmount = 0;
	pBinWeight = NULL;
}

void CBoundedValueHistogram::myInit(int BinAmount, double* pBinWeight)
{
	this->BinAmount = BinAmount;
	this->pBinWeight = pBinWeight;
}

void CBoundedValueHistogram::SetParameters(int Mode, double ValueBound_A, double ValueBound_B)
{
	this->Mode = Mode;
	if (ValueBound_A < ValueBound_B)
	{
		ValueBase = ValueBound_A;
		ValuePerBin = ValueBound_B;
	}
	else
	{
		ValueBase = ValueBound_B;
		ValuePerBin = ValueBound_A;
	}
	ValuePerBin -= ValueBase - EPS;
	if (ValuePerBin < EPS)
	{
		ValueBase -= 1.0;
		ValuePerBin += 2.0;
	}
	ValuePerBin /= BinAmount;
	Clear();
}

void CBoundedValueHistogram::Clear()
{
	memset(pBinWeight, 0, sizeof(double) * BinAmount);
	WeightSum = 0.0;
}

int CBoundedValueHistogram::SetIndexBounded(int Index)
{
	return (Index<0 ? 0 : (Index>=BinAmount ? BinAmount-1 : Index));
}

double CBoundedValueHistogram::GetThreshold_SmallerThan(int BinIndex)
{
	return ValueBase + ValuePerBin * BinIndex - EPS;
}

double CBoundedValueHistogram::GetThreshold_BiggerThan(int BinIndex)
{
	return ValueBase + (ValuePerBin + 1) * BinIndex + EPS;
}

int CBoundedValueHistogram::GetIndex_Floor(double Value)
{
	return SetIndexBounded((int)(((double)(Value - ValueBase)) / ValuePerBin));
}

double CBoundedValueHistogram::GetIndex_Between(double Value, int& i, int& j)
{
	double t = ((double)(Value - ValueBase)) / ValuePerBin + 0.5 - EPS;
	j = (int)t;
	i = SetIndexBounded(j-1);
	j = SetIndexBounded(j);
	if (i == j)
		return 1.0;
	else
		return t - j;
} 

void CBoundedValueHistogram::PushValue(double Value, double Weight)
{
	if (Mode & INTERPOLATING_TYPE_SINGLE_BIN)
	{
		int i = GetIndex_Floor(Value);
		pBinWeight[i] += Weight;
		WeightSum += Weight;
	}
	else if (Mode & INTERPOLATING_TYPE_DOUBLE_BIN_LINEAR)
	{
		int i,j;
		double w;
		w = GetIndex_Between(Value, i, j);
		pBinWeight[i] +=    w  * Weight;
		pBinWeight[j] += (1-w) * Weight;
		WeightSum += Weight;
	}
	else
	{

	}
}

void CBoundedValueHistogram::CalcWeightSum()
{
	int i;
	double* pa;
	WeightSum = 0;
	pa = pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
		WeightSum += *(pa++);
}

void CBoundedValueHistogram::LetEquals(const CBoundedValueHistogram& b)
{
	int i;
	double* pa;
	const double* pb;

	if (BinAmount != b.BinAmount) return;
	WeightSum = b.WeightSum;
	pa = pBinWeight; pb = b.pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
		*(pa++) = *(pb++);
}

void CBoundedValueHistogram::AddEquals(const CBoundedValueHistogram& b)
{
	int i;
	double* pa;
	const double* pb;

	if (BinAmount != b.BinAmount) return;
	WeightSum += b.WeightSum;
	pa = pBinWeight; pb = b.pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
		*(pa++) += *(pb++);
}

void CBoundedValueHistogram::SubEquals(const CBoundedValueHistogram& b)
{
	int i;
	double* pa;
	const double* pb;

	if (BinAmount != b.BinAmount) return;
	WeightSum -= b.WeightSum;
	pa = pBinWeight; pb = b.pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
		*(pa++) -= *(pb++);
}

void CBoundedValueHistogram::MulEquals(const CBoundedValueHistogram& b)
{
	int i;
	double* pa;
	const double* pb;

	if (BinAmount != b.BinAmount) return;
	WeightSum = 0;
	pa = pBinWeight; pb = b.pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
	{
		*pa *= *(pb++);
		WeightSum += *(pa++);
	}
}

void CBoundedValueHistogram::MulEquals(double num)
{
	int i;
	double* pa;

	WeightSum *= num;
	pa = pBinWeight;
	for ( i = 0; i < BinAmount; ++i )
		*(pa++) *= num;
}

// [*] CIVPair

bool CIVPair::CompareByIndex( const CIVPair& a, const CIVPair& b )
{
	return a.Index < b.Index;
}

bool CIVPair::CompareByValue( const CIVPair& a, const CIVPair& b )
{
	return a.Value < b.Value;
}

